use rand::Rng;

use crate::Vector3::Vector3;
pub struct MathUtils;

impl MathUtils {
    pub const Deg2Rad: f64 = std::f64::consts::PI / 180.0;
    pub const Rad2Deg: f64 = 180.0 / std::f64::consts::PI;
    pub const Epsilon: f64 = 0.000001;

    pub fn equal_v3(v: &Vector3, v1: &Vector3, epsilon: Option<f64>) -> bool {
        MathUtils::equal(v.x, v1.x, epsilon)
            && MathUtils::equal(v.y, v1.y, epsilon)
            && MathUtils::equal(v.z, v1.z, epsilon)
    }

    pub fn equal_0(a: f64, epsilon: Option<f64>) -> bool {
        let epsilon = epsilon.unwrap_or(MathUtils::Epsilon);
        a <= epsilon && a >= -epsilon
    }

    pub fn equal(a: f64, b: f64, epsilon: Option<f64>) -> bool {
        MathUtils::equal_0(a - b, epsilon)
    }

    pub fn greater(a: f64, b: f64, epsilon: Option<f64>) -> bool {
        let epsilon = epsilon.unwrap_or(MathUtils::Epsilon);
        a - b > epsilon
    }

    pub fn greater_equal(a: f64, b: f64, epsilon: Option<f64>) -> bool {
        !MathUtils::less(a, b, epsilon)
    }

    pub fn less(a: f64, b: f64, epsilon: Option<f64>) -> bool {
        MathUtils::greater(b, a, epsilon)
    }

    pub fn less_equal(a: f64, b: f64, epsilon: Option<f64>) -> bool {
        !MathUtils::greater(a, b, epsilon)
    }

    pub fn sign(value: f64) -> i32 {
        if value < 0.0 {
            return -1;
        }
        if value > 0.0 {
            return 1;
        }
        return 0;
    }

    pub fn overlap(a: f64, b: f64, c: f64, d: f64, ep: Option<f64>) -> Option<[f64; 2]> {
        let (mut x1, mut x2, mut y1, mut y2): (f64, f64, f64, f64);
        if a < b {
            x1 = a;
            x2 = b;
        } else {
            x1 = b;
            x2 = a;
        }
        if c < d {
            y1 = c;
            y2 = d;
        } else {
            y1 = d;
            y2 = c;
        }
        if MathUtils::greater_equal(y1, x1, ep) && MathUtils::less_equal(y1, x2, ep) {
            if MathUtils::less_equal(y2, x2, ep) {
                return Some([y1, y2]);
            } else {
                return Some([y1, x2]);
            }
        } else {
            if MathUtils::less(y1, x1, ep) {
                if MathUtils::greater_equal(y2, x1, ep) {
                    if MathUtils::less_equal(y2, x2, ep) {
                        return Some([x1, y2]);
                    } else {
                        return Some([x1, x2]);
                    }
                }
            }
        }
        None
    }

    pub fn get_random_int(min: i32, max: i32) -> i32 {
        let mut rng = rand::thread_rng();
        rng.gen_range(min..=max)
    }

    pub fn get_random_arbitrary(min: f64, max: f64) -> f64 {
        let mut rng = rand::thread_rng();
        let r_f = rng.gen_range(0.0..=1.0);
        min + (max - min) * r_f
    }
}
